package net.expectx.micro.shop.api.authorization.interceptor;

import com.alibaba.fastjson.JSON;

import net.expectx.common.single.util.Md5Util;
import net.expectx.micro.shop.api.authorization.annotation.Authorization;
import net.expectx.micro.shop.api.authorization.annotation.NoEncryption;
import net.expectx.micro.shop.api.common.ApiConstants;
import net.expectx.micro.shop.api.common.ApiResult;
import net.expectx.micro.shop.api.common.ApiResultConstants;
import net.expectx.micro.shop.api.manager.RedisTokenSingle;
import net.expectx.micro.shop.api.manager.TokenManager;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.http.MediaType;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;


/**
 * 自定义拦截器，对请求进行身份验证
 * @author lijian
 * @date 2017-11-03
 */
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
    private static final Logger _LOGGER = LoggerFactory.getLogger(AuthorizationInterceptor.class);
    TokenManager tokenManager;
    /**
     * 存放鉴权信息的Header名称，Token
     */
    private String accessTokenHeaderName = "Access-Token";
    /**
     * 获得请求语言
     */
    private String languageHeaderName="Accept-Language";

    /**
     * 操作环境
     */
    private String userAgentHeaderName="User-Agent";

    /**
     * AccessKey ID
     */
    private String appKeyHeaderName="App-Key";
    /**
     * Access Key Secret
     */
    private String appSecretHeaderName="App-Secret";
    /**
     * 加密字符串
     */
    private String encryptHeaderName="Encrypt";
    Byte encryptionTime=1;
    String encryptionCode="ExpectX";


    /**
     * 鉴权信息的无用前缀，默认为空
     */
    private String httpHeaderPrefix = "";

    /**
     * 鉴权失败后返回的错误信息，默认为401 unauthorized
     */
    private String unauthorizedErrorMessage = "令牌失效，请重新登录";

    /**
     * 鉴权失败后返回的HTTP错误码，默认为401
     */
    private int unauthorizedErrorCode = HttpServletResponse.SC_UNAUTHORIZED;


    public void setAccessTokenHeaderName(String accessTokenHeaderName) {
        this.accessTokenHeaderName = accessTokenHeaderName;
    }
    public void setHttpHeaderPrefix(String httpHeaderPrefix) {
        this.httpHeaderPrefix = httpHeaderPrefix;
    }

    public void setUnauthorizedErrorMessage(String unauthorizedErrorMessage) {

        this.unauthorizedErrorMessage = unauthorizedErrorMessage;

    }

    public void setUnauthorizedErrorCode(int unauthorizedErrorCode) {

        this.unauthorizedErrorCode = unauthorizedErrorCode;

    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //如果不是映射到方法直接通过
        if(!(handler instanceof HandlerMethod)) {
            return true;
        }
        tokenManager= RedisTokenSingle.getInstance().tokenManager();
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        /**
         * 开启方法是否参与加密
         */
        if(method.getAnnotation(NoEncryption.class) == null){
            String encrypt=request.getHeader(encryptHeaderName);
            if(StringUtils.isNotEmpty(encrypt)){
                /**
                 * 判断是否有参数传入
                 */
                Enumeration<String> enumeration=request.getParameterNames();
                if (enumeration.hasMoreElements()){
                    /**
                     * 获得参数并字典排序
                     */
                    List<String>parameterValueList=new LinkedList<>();
                    while (enumeration.hasMoreElements()){
                        String parameterName=enumeration.nextElement();
                        parameterValueList.add(request.getParameter(parameterName));
                    }
                    parameterValueList.add(encryptionCode);
                    String [] parameterValues=new String[parameterValueList.size()];
                    for(int i=0,j=parameterValueList.size();i<j;i++){
                        parameterValues[i]=parameterValueList.get(i);
                    }
                    Arrays.sort(parameterValues);
                    StringBuffer parameterValue=new StringBuffer();
                    for (int i = 0; i < parameterValues.length; i++) {
                        parameterValue.append(parameterValues[i]);
                    }
                    String tempEncrypt= Md5Util.md5(parameterValue.toString()).toLowerCase();

                    for (int i=0;i<encryptionTime-1;i++){
                        tempEncrypt= Md5Util.md5(tempEncrypt).toLowerCase();
                        _LOGGER.info("第"+(i+1)+"次加密:"+tempEncrypt);
                    }
                    _LOGGER.info("Encrypt String:"+tempEncrypt.toLowerCase());

                    if (!encrypt.toLowerCase().equals(tempEncrypt.toLowerCase())){
                        response.setStatus(unauthorizedErrorCode);
                        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(response.getOutputStream()));
                        bufferedWriter.write(JSON.toJSONString(new ApiResult(Boolean.FALSE,ApiResultConstants.PARAMETER_MANIPULATION, null)));
                        bufferedWriter.close();
                        return false;
                    }
                }
            }else {
                response.setStatus(unauthorizedErrorCode);
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(response.getOutputStream()));
                bufferedWriter.write(JSON.toJSONString(new ApiResult(Boolean.FALSE,ApiResultConstants.HEADER_NO_ENCRYPT_PARAM, null)));
                bufferedWriter.close();
                return false;
            }
        }
        BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
        //从header中得到token
        String token = request.getHeader(accessTokenHeaderName);
        if(token != null && token.startsWith(httpHeaderPrefix) && token.length() > 0) {
            token = token.substring(httpHeaderPrefix.length());
            //验证token
            String key = tokenManager.getKey(token);
            if(key != null) {
                //如果token验证成功，将token对应的用户id存在request中，便于之后注入
                request.setAttribute(ApiConstants.REQUEST_CURRENT_KEY, key);
                return true;
            }
        }

        /*
         *如果验证token失败，并且方法注明了Authorization，返回401错误
         *查看方法上是否有注解
         *查看方法所在的Controller是否有注解
         */
        if(method.getAnnotation(Authorization.class) != null ||
                handlerMethod.getBeanType().getAnnotation(Authorization.class) != null){

            response.setStatus(unauthorizedErrorCode);
            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(response.getOutputStream()));
            bufferedWriter.write(JSON.toJSONString(new ApiResult(Boolean.FALSE,ApiResultConstants.ERROR_UNAUTHORIZED, null)));
            bufferedWriter.close();
            return false;
        }
        //为了防止以恶意操作直接在REQUEST_CURRENT_KEY写入key，将其设为null
        request.setAttribute(ApiConstants.REQUEST_CURRENT_KEY, null);
        return true;
    }

}
